package com.sxt.practiceapplication.utils.camera;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.sxt.practiceapplication.utils.PermissionManager;
import com.sxt.practiceapplication.utils.ToastUtils;
import com.sxt.practiceapplication.utils.runtimepermissions.PermissionsManager;
import com.sxt.practiceapplication.utils.runtimepermissions.PermissionsResultAction;


import java.util.ArrayList;


public class CameraUtil {

    private final Context context;

    private final TextureView mTextureView;

    private final DefaultSurfaceTextureListener defaultSurfaceTextureListener;

    private Size mPreviewSize;

    private String mCameraId;

    private final CameraDevice.StateCallback stateCallback;

    private CameraDevice mCameraDevice;

    private CaptureRequest.Builder mCaptureRequestBuilder;

    public ImageReader mImageReader;

    private CaptureRequest mCaptureRequest;

    private final CameraCaptureSession.CaptureCallback mSessionCaptureCallback;

    private CameraCaptureSession mPreviewSession;

    private final CameraConfig cameraConfig;

    private final CameraManager cameraManager;

    private HandlerThread handlerThread;

    public Handler handler;

    private PreviewCallback previewCallback;

    private boolean isCameraStart;

    public CameraUtil(Context context, TextureView mTextureView) {
        this(context, mTextureView, null);
    }

    public CameraUtil(Context context, TextureView mTextureView, CameraConfig cameraConfig) {
        this.context = context;
        this.mTextureView = mTextureView;
        if (cameraConfig != null) {
            this.cameraConfig = cameraConfig;
        } else {
            this.cameraConfig = new CameraConfig();
            this.cameraConfig.cameraFacing = CameraConfig.FRONT_FACING_CAMERA;
        }
        this.defaultSurfaceTextureListener = new DefaultSurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
                //当SurfaceTexture可用的时候，设置相机参数并打开相机
                setupCamera(width, height);
                this.textureWidth = width;
                this.textureHeight = height;
                openCamera();
            }
        };
        this.stateCallback = new CameraDevice.StateCallback() {
            @Override
            public void onOpened(CameraDevice camera) {
                mCameraDevice = camera;
                //开启预览
                startPreview();
            }

            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {
                camera.close();
                mCameraDevice = null;
            }

            @Override
            public void onError(@NonNull CameraDevice camera, int error) {
                camera.close();
                mCameraDevice = null;
            }
        };
        this.mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        };
        //获取摄像头的管理者CameraManager
        cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    }

    @SuppressWarnings("unused")
    public void initSurfaceTexture(DefaultSurfaceTextureListener surfaceTextureListener) {
        mTextureView.setSurfaceTextureListener(surfaceTextureListener);
    }

    public void initSurfaceTexture() {
        mTextureView.setSurfaceTextureListener(defaultSurfaceTextureListener);
    }

    private void setupCamera(int width, int height) {
        setupCamera(width, height, -1);
    }

    private void setupCamera(int width, int height, int cameraFacing) {
        getCamera(cameraFacing < 0 ? cameraConfig.cameraFacing : cameraFacing, width, height);
    }

    public void openCamera() {
        initHandlerThread();
        //检查权限
        try {
            PermissionManager permissionManager = PermissionManager.getInstance(context);
            if (!permissionManager.checkCameraPermission()) {
                permissionManager.getCameraPermission();
                if (!permissionManager.checkCameraPermission()) {
                    return;
                }
            }
            //根据屏幕的显示方向调整预览方向
            configureTransform(defaultSurfaceTextureListener.textureWidth, defaultSurfaceTextureListener.textureHeight);
            //打开相机，第一个参数指示打开哪个摄像头，第二个参数stateCallback为相机的状态回调接口，第三个参数用来确定Callback在哪个线程执行，为null的话就在当前线程执行
            cameraManager.openCamera(mCameraId, stateCallback, handler);
            isCameraStart = true;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void startPreview() {
        SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
        //设置TextureView的缓冲区大小
        mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

        //创建CaptureRequestBuilder，TEMPLATE_PREVIEW比表示预览请求
        try {
            mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        //获取Surface显示预览数据
        Surface mSurface = new Surface(mSurfaceTexture);
        ArrayList<Surface> surfaces = new ArrayList<>();
        surfaces.add(mSurface);
        //设置Surface作为预览数据的显示界面
        mCaptureRequestBuilder.addTarget(mSurface);
//        if (previewCallback != null) {
            setupImageReader();
            //获取ImageReader的Surface
            Surface imageReaderSurface = mImageReader.getSurface();
            surfaces.add(imageReaderSurface);
//            mCaptureRequestBuilder.addTarget(imageReaderSurface);
//        }

        try {
            //创建相机捕获会话，第一个参数是捕获数据的输出Surface列表，第二个参数是CameraCaptureSession的状态回调接口，当它创建好后会回调onConfigured方法，第三个参数用来确定Callback在哪个线程执行，为null的话就在当前线程执行
            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        //创建捕获请求
                        mCaptureRequest = mCaptureRequestBuilder.build();
                        mPreviewSession = session;
                        //设置反复捕获数据的请求，这样预览界面就会一直有数据显示
                        session.setRepeatingRequest(mCaptureRequest, mSessionCaptureCallback, handler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {

                }
            }, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void setupImageReader() {
        //前三个参数分别是需要的尺寸和格式，最后一个参数代表每次最多获取几帧数据，本例的2代表ImageReader中最多可以获取两帧图像流
        mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
                ImageFormat.JPEG, 10);
        //监听ImageReader的事件，当有图像流数据可用时会回调onImageAvailable方法，它的参数就是预览帧数据，可以对这帧数据进行处理
        if (previewCallback != null) {
            previewCallback.setPreviewCallback(mImageReader, handler);
        }
    }

    @SuppressWarnings("unused")
    public void setPreviewCallback(PreviewCallback callback) {
        previewCallback = callback;
    }

    public interface PreviewCallback {
        void setPreviewCallback(ImageReader mImageReader, Handler handler);
    }

    private void getCamera(int cameraFacing, int width, int height) {
        try {
            //遍历所有摄像头
            for (String cameraId : cameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
                //默认打开后置摄像头
                if (characteristics.get(CameraCharacteristics.LENS_FACING) != cameraFacing)
                    continue;
                //获取StreamConfigurationMap，它是管理摄像头支持的所有输出格式和尺寸
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                //根据TextureView的尺寸设置预览尺寸
                mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                mCameraId = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    public void switchCameraFacing() {
        setupCamera(defaultSurfaceTextureListener.textureWidth,
                defaultSurfaceTextureListener.textureHeight,
                cameraConfig.cameraFacing == CameraConfig.FRONT_FACING_CAMERA ?
                        (cameraConfig.cameraFacing = CameraConfig.BACK_CAMERA) :
                        (cameraConfig.cameraFacing = CameraConfig.FRONT_FACING_CAMERA));
        onPause();
        openCamera();
    }

    public void onPause() {
        if (mPreviewSession != null) {
            try {
                mPreviewSession.stopRepeating();
                mPreviewSession.close();
                mPreviewSession = null;
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
    }

    public void closeCamera() {
        release();
        stopHandlerThread();
        isCameraStart = false;
    }

    public boolean isCameraStarted() {
        return isCameraStart;
    }


    //最好在 onPause 中调用，如果在 onDestroy 中调用，CameraDevice 会优先被系统关闭
    //此时 session.close()　会抛状态异常
    private void release() {

        if (mPreviewSession != null) {
            if (mCameraDevice != null) {
                mPreviewSession.close();
            }
            mPreviewSession = null;
        }

        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        if (mImageReader != null) {
            mImageReader.close();
            mImageReader = null;
        }

        handler = null;
    }

    public static Size getOptimalSize(@NonNull Size[] sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) h / w;
        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        for (Size size : sizes) {
            double ratio = (double) size.getWidth() / size.getHeight();
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.getHeight() - h) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.getHeight() - h);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.getHeight() - h) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.getHeight() - h);
                }
            }
        }

        return optimalSize;
    }

    private void initHandlerThread() {
        handlerThread = new HandlerThread("CameraThread");
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper());
    }

    private void stopHandlerThread() {
        handlerThread.quitSafely();
        try {
            handlerThread.join();
            handlerThread = null;
            handler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void configureTransform(int viewWidth, int viewHeight) {
        Activity activity = (Activity) context;
        if (null == mTextureView || null == mPreviewSize || null == activity) {
            return;
        }
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        Matrix matrix = new Matrix();
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
        float centerX = viewRect.centerX();
        float centerY = viewRect.centerY();
        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
            float scale = Math.max(
                    (float) viewHeight / mPreviewSize.getHeight(),
                    (float) viewWidth / mPreviewSize.getWidth());
            matrix.postScale(scale, scale, centerX, centerY);
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
        } else if (Surface.ROTATION_180 == rotation) {
            matrix.postRotate(180, centerX, centerY);
        }
        mTextureView.setTransform(matrix);
    }

    public void capture() {
        if (!isCameraStart) {
            return;
        }
        try {
            //首先我们创建请求拍照的CaptureRequest
            final CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            mCaptureBuilder.addTarget(mImageReader.getSurface());
            //这个回调接口用于拍照结束时重启预览，因为拍照会导致预览停止
            CameraCaptureSession.CaptureCallback mImageSavedCallback = new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
//                    Toast.makeText(CameraUtil.this.context, "Image Saved!", Toast.LENGTH_SHORT).show();
                    //重启预览
//                    restartPreview();
                }
            };
            //停止预览
//            mPreviewSession.stopRepeating();
            //开始拍照，然后回调上面的接口重启预览，因为mCaptureBuilder设置ImageReader作为target，所以会自动回调ImageReader的onImageAvailable()方法保存图片
            mPreviewSession.capture(mCaptureBuilder.build(), mImageSavedCallback, handler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

//    private void restartPreview() {
//        try {
//            //执行setRepeatingRequest方法就行了，注意mCaptureRequest是之前开启预览设置的请求
//            mPreviewSession.setRepeatingRequest(mCaptureRequest, null, handler);
//        } catch (CameraAccessException e) {
//            e.printStackTrace();
//        }
//    }

}
